package com.mlh.myspring.annotation;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.File;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;

//充当容器类,类似 Spring 原生的 ApplicationContext
public class MlhSpringApplicationContext {
    private Class config;
    private final ConcurrentHashMap<String,Object> ioc=new ConcurrentHashMap<>();
    //构造器
    public MlhSpringApplicationContext(Class config){
        this.config=config;
        //1. 解析配置类
        //2. 获取到配置类的注解 @ComponentScan("com.mlh.spring.component")
        ComponentScan componentScan =(ComponentScan)this.config.getDeclaredAnnotation(ComponentScan.class);
        String path=componentScan.value();
        //3. 获取扫描路径下所有的类文件
        //(1) 先得到类加载器, 使用 App 方式来加载.
        ClassLoader classLoader = MlhSpringApplicationContext.class.getClassLoader();
        //在获取某个包的对应的 URL 时，要求是 com/hspedu/spring/component
        //URL resource = classLoader.getResource("com/hspedu/spring/component");
        //(2) 将 path 转成 形式为 com/hspedu/spring/component
        path=path.replace(".","/");
        //(3) 这 里 是 获 取 到 我 们 要 加 载 的 资 源 ( 类 ) 的 路 径 ( 工 作 路 径 :
        //file:/D:/hspedu_ssm_temp/spring5/out/production/spring5/com/hspedu/spring/component)
        //获得工作路径的目的是为了扫描该路径下的类(为什么不扫描源文件呢？我是这样理解的：因为工作路径中存在，项目中才是真的可以创建)
        URL resource=classLoader.getResource(path);
        File file = new File(resource.getFile());
        if(file.isDirectory()){
            for (File f : file.listFiles()) {
                String fileAbsolutePath=f.getAbsolutePath();
                if(fileAbsolutePath.endsWith(".class")){//说明是类文件
                    //通过类加载器获取来类文件的 Clazz 对象
                    // 先 得 到 类 的 完 整 类 路 径 形 式 为 com.hspedu.spring.component.MonsterService
                    //获得类名
                    String className=fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1,
                            fileAbsolutePath.indexOf(".class"));
                    String classFullPath = path.replace("/", ".") + "." + className;
                    try {
                        //获取到扫描包下的类的 clazz 对象
                        Class<?> clazz = classLoader.loadClass(classFullPath);
                        if (clazz.isAnnotationPresent(Component.class)//如果这个类有@Commponent, 说明是一个 springbean
                                || clazz.isAnnotationPresent(Service.class)
                                || clazz.isAnnotationPresent(Repository.class)
                                || clazz.isAnnotationPresent(Controller.class)) {
                            Class<?> aClass = Class.forName(classFullPath);

                            try {
                                //将其反射生成到 ioc 中
                                Object instance = aClass.newInstance();
                                //StringUtils.uncapitalize(className)让其首字母小写
                                ioc.put(StringUtils.uncapitalize(className),instance);
                            }catch (InstantiationException e){
                                e.printStackTrace();
                            }catch (IllegalAccessException e){
                                e.printStackTrace();
                            }

                        }else{
                            System.out.println("不是一个 bean = " + clazz);
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }

                }
            }

        }

    }
    //返回 ioc 注入的指定 bean
    public Object getBean(String name){
        return ioc.get(name);
    }
}
